#include "t_box_input_youtube.h"
#include "t_youtube.h"
#include "t_httpbackend.h"

class T_Box_Input_YoutubePrivate : public QObject
{
public:
    explicit T_Box_Input_YoutubePrivate(T_Box_Input_Youtube *me)
        : QObject(me), q_ptr(me)
        , m_youtube(&m_nam)
        , m_waiter(0)
        , m_cont_len(-1)
    {
    }
    virtual ~T_Box_Input_YoutubePrivate()
    {
        delete m_waiter;
    }
    //[public]
    Q_DECLARE_PUBLIC(T_Box_Input_Youtube)
    class T_Box_Input_Youtube *q_ptr;
    //[private]
    T_NetworkAccessManager m_nam;
    T_Youtube m_youtube;
    NetworkBuffer *m_waiter;
    qint64 m_cont_len;
    QString m_video_id;
    int m_fmt;
    QString m_file_suffix;
    QNetworkRequest m_request;
    QByteArray m_file_bytes;
    QBuffer m_file;
    QFile m_cache_file;
    QNetworkReply *m_reply;
    //[methods]
    QString cache_file_name()
    {
        QString v_name = m_video_id + "_" + QString::number(m_fmt);
        if(m_cont_len >= 0)
        {
            v_name += "_" + QString::number(m_cont_len);
        }
        v_name += "." + m_file_suffix;
        QString v_dirPath =
                QDesktopServices::storageLocation(
                        QDesktopServices::DesktopLocation);
        QDir dir(v_dirPath);
        QString v_filePath = dir.filePath(v_name);
        return v_filePath;
    }
    QNetworkReply *get_video_stream_head()
    {
        QVariant v_loc = m_youtube.getVideoStreamUrl(m_video_id, m_fmt);
        NetworkBatch v_nbat(&m_nam);
        QNetworkReply *v_head_reply = NULL;
        while(v_loc.isValid())
        {
            delete v_head_reply;
            qDebug() << "[v_loc]" << v_loc;
            v_head_reply = v_nbat.head(v_loc.toUrl());
            v_loc = v_head_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
        }

        QVariant v_cont_header = v_head_reply->header(QNetworkRequest::ContentTypeHeader);
        if(!v_cont_header.isValid())
        {
            qDebug() << "(!v_cont_header.isValid())";
            delete v_head_reply;
            return NULL;
        }

        qDebug() << v_cont_header;

        if(!v_cont_header.toString().startsWith("video/"))
        {
            qDebug() << "(!v_cont_header.toString().startsWith(\"video/\"))";
            delete v_head_reply;
            return NULL;
        }

        m_file_suffix = v_cont_header.toString();
        m_file_suffix.replace("video/", "");
        qDebug() << "ok";

        return v_head_reply;
    }
};

T_Box_Input_Youtube::T_Box_Input_Youtube(const QString &a_video_id, int a_fmt)
{
    this->d_ptr = new T_Box_Input_YoutubePrivate(this);
    Q_D(T_Box_Input_Youtube);
    m_is_valid = true;

    d->m_video_id = a_video_id;
    d->m_fmt = a_fmt;

    m_media_name = QString("T_Box_Input_Youtube(%1, %2)").arg(a_video_id).arg(a_fmt);

    QNetworkReply *v_head_reply = d->get_video_stream_head();

    if(!v_head_reply)
    {
        m_is_valid = false;
        return;
    }

    ////QNetworkReply *v_head_reply = v_nbat.head(v_head_reply->url());
    QVariant v_cont_len = v_head_reply->header(QNetworkRequest::ContentLengthHeader);
    if(!v_cont_len.isValid())
    {
        //FIXME: case m_cont_len is not available
        d->m_cont_len = -1;
    }
    else
    {
        d->m_cont_len = v_cont_len.toLongLong();
        qDebug() << "[m_cont_len]" << d->m_cont_len;
    }

    d->m_request.setUrl(v_head_reply->url());
    d->m_request.setHeader(
            QNetworkRequest::CookieHeader,
            QVariant::fromValue(d->m_nam.cookieJar()->cookiesForUrl(v_head_reply->url()))
            );

    delete v_head_reply;

    qDebug() << "[d->cache_file_name()]" << d->cache_file_name();
    d->m_cache_file.setFileName(d->cache_file_name());
    if(!d->m_cache_file.open(QIODevice::ReadWrite))
    {
        qDebug() << "(!d->m_cache_file.open(QIODevice::ReadWrite))";
        m_is_valid = false;
        return;
    }

    d->m_file_bytes = d->m_cache_file.readAll();
    qDebug() << "[d->m_cache_file.size()]" << d->m_cache_file.size();
    qDebug() << "[d->m_file_bytes.size()]" << d->m_file_bytes.size();
    d->m_file.setBuffer(&d->m_file_bytes);
    d->m_file.open(QIODevice::ReadOnly);

    //[Range header]
    QString v_range = QString("bytes=%1-").arg(d->m_file.size());
    qDebug() << v_range;
    d->m_request.setRawHeader("Range", v_range.toLatin1());
    //
    d->m_reply = d->m_nam.get(d->m_request);
    d->m_waiter = new NetworkBuffer(d->m_reply);
    d->m_waiter->setName(QString("g.waiter@%1").arg(d->m_file.size()));
}

T_Box_Input_Youtube::~T_Box_Input_Youtube()
{
    //Q_D(T_Box_Input_Youtube);
}

bool T_Box_Input_Youtube::isValid()
{
    T_ATOMIC_LOCKER(v_lock, &m_mutex)
    return m_is_valid;
}

void T_Box_Input_Youtube::close()
{
    Q_D(T_Box_Input_Youtube);
    T_ATOMIC_LOCKER(v_lock, &m_mutex)
    if(!d->m_file.isOpen()) return;
    m_is_valid = false;
}

int T_Box_Input_Youtube::bufferSize()
{
    return c_buffer_size;
}

//int64_t T_Box_Input_Youtube::seek_length()
qint64 T_Box_Input_Youtube::size()
{
    Q_D(T_Box_Input_Youtube);
    T_ATOMIC_LOCKER(v_lock, &m_mutex)
    Q_UNUSED(v_lock);
    return d->m_cont_len;
}

//int64_t T_Box_Input_Youtube::seek_getpos()
qint64 T_Box_Input_Youtube::pos()
{
    Q_D(T_Box_Input_Youtube);
    T_ATOMIC_LOCKER(v_lock, &m_mutex)
    return d->m_file.pos();
}

//int64_t T_Box_Input_Youtube::seek_setpos(int64_t offset)
bool T_Box_Input_Youtube::seek(qint64 offset)
{
    Q_D(T_Box_Input_Youtube);
    T_ATOMIC_LOCKER(v_lock, &m_mutex)
    if(!isValid()) return -1;
    //qDebug() << "[offset]" << offset;
    //qDebug() << "[d->m_file.pos()]" << d->m_file.pos();
    qint64 v_diff = offset - d->m_file.pos();
    if(v_diff > 0)
    {
        qint64 v_avail = d->m_file.bytesAvailable();
        if(v_avail < v_diff)
        {
            int v_wait = (v_diff-v_avail);
            //qDebug() << "[v_diff]" << v_diff << "[v_avail]" << v_avail;
            //qDebug() << "[v_wait]" << v_wait;
            QByteArray v_append = d->m_waiter->wait_readAll(v_wait);
            d->m_cache_file.write(v_append);
            d->m_file_bytes.append(v_append);
        }
    }
    return d->m_file.seek(offset);
}

//int T_Box_Input_Youtube::read_bytes(uint8_t *buf, int buf_size)
QByteArray T_Box_Input_Youtube::wait_read(qint64 a_max_size)
{
    Q_D(T_Box_Input_Youtube);
    T_ATOMIC_LOCKER(v_lock, &m_mutex)
    if(!isValid()) return 0;
    Q_ASSERT(d->m_file.isOpen());
    Q_ASSERT(d->m_cache_file.isOpen());

    qint64 v_diff = a_max_size;
    qint64 v_avail = d->m_file.bytesAvailable();
    if(v_avail < v_diff)
    {
        int v_wait = (v_diff-v_avail);
        QByteArray v_append = d->m_waiter->wait_readAll(v_wait);
        d->m_cache_file.write(v_append);
        d->m_file_bytes.append(v_append);
    }
    return d->m_file.read(a_max_size);
}
